/*
  Copyright (c) 2009-2017 Dave Gamble and zJSON contributors

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/

#ifndef zJSON__h
#define zJSON__h

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>

#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
    #define __WINDOWS__
#endif

#ifdef __WINDOWS__
    /* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention.  For windows you have 3 define options:

    zJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
    zJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
    zJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol

    For *nix builds that support visibility attribute, you can define similar behavior by

    setting default visibility to hidden by adding
    -fvisibility=hidden (for gcc)
    or
    -xldscope=hidden (for sun cc)
    to CFLAGS

    then using the zJSON_API_VISIBILITY flag to "export" the same symbols the way zJSON_EXPORT_SYMBOLS does

    */

    #define zJSON_CDECL __cdecl
    #define zJSON_STDCALL __stdcall

    /* export symbols by default, this is necessary for copy pasting the C and header file */
    #if !defined(zJSON_HIDE_SYMBOLS) && !defined(zJSON_IMPORT_SYMBOLS) && !defined(zJSON_EXPORT_SYMBOLS)
        #define zJSON_EXPORT_SYMBOLS
    #endif

    #if defined(zJSON_HIDE_SYMBOLS)
        #define zJSON_PUBLIC(type)   type zJSON_STDCALL
    #elif defined(zJSON_EXPORT_SYMBOLS)
        #define zJSON_PUBLIC(type)   __declspec(dllexport) type zJSON_STDCALL
    #elif defined(zJSON_IMPORT_SYMBOLS)
        #define zJSON_PUBLIC(type)   __declspec(dllimport) type zJSON_STDCALL
    #endif
#else /* !__WINDOWS__ */
    #define zJSON_CDECL
    #define zJSON_STDCALL

    #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(zJSON_API_VISIBILITY)
    #define zJSON_PUBLIC(type)   __attribute__((visibility("default"))) type
    #else
    #define zJSON_PUBLIC(type) type
    #endif
#endif

/* project version */
#define zJSON_VERSION_MAJOR 1
#define zJSON_VERSION_MINOR 7
#define zJSON_VERSION_PATCH 18

#include <stddef.h>

/* zJSON Types: */
typedef enum
{
    zJSON_Invalid = 0,
    zJSON_Bool,
    zJSON_NULL,
    zJSON_Number,
    zJSON_String,
    zJSON_Key,
    zJSON_Value,
    zJSON_Array,
    zJSON_Object,
    zJSON_Raw,
}zJSON_Type;

#define zJSON_IsReference 256
#define zJSON_StringIsConst 512

/* The zJSON structure: */
typedef struct zJSON
{
    /* The type of the item, as above. */
    int type;
    const char* content;
    size_t length;
    size_t offset;
} zJSON;

/* Limits how deeply nested arrays/objects can be before zJSON rejects to parse them.
 * This is to prevent stack overflows. */
#ifndef zJSON_NESTING_LIMIT
#define zJSON_NESTING_LIMIT 1000
#endif

/* returns the version of zJSON as a string */
zJSON_PUBLIC(const char*) zJSON_Version(void);

/* Supply malloc, realloc and free functions to zJSON */

/* Memory Management: the caller is always responsible to free the results from all variants of zJSON_Parse (with zJSON_Delete) and zJSON_Print (with stdlib free, zJSON_Hooks.free_fn, or zJSON_free as appropriate). The exception is zJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
/* Supply a block of JSON, and this returns a zJSON object you can interrogate. */
zJSON_PUBLIC(zJSON) zJSON_Parse(const char* value);
zJSON_PUBLIC(zJSON) zJSON_ParseWithLength(const char* value, size_t buffer_length);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match zJSON_GetErrorPtr(). */
zJSON_PUBLIC(zJSON) zJSON_ParseWithOpts(const char* value, const char** return_parse_end,
                                          bool require_null_terminated);
zJSON_PUBLIC(zJSON) zJSON_ParseWithLengthOpts(const char* value, size_t buffer_length, const char** return_parse_end,
                                                bool require_null_terminated);

/* Render a cJSON entity to text for transfer/storage. */
zJSON_PUBLIC(char *) cJSON_Print(const zJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. */
zJSON_PUBLIC(char *) cJSON_PrintUnformatted(const zJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
zJSON_PUBLIC(char *) cJSON_PrintBuffered(const zJSON *item, int prebuffer, bool fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
zJSON_PUBLIC(bool) cJSON_PrintPreallocated(zJSON *item, char *buffer, const int length, const bool format);
/* Delete a cJSON entity and all subentities. */
zJSON_PUBLIC(void) cJSON_Delete(zJSON *item);

/* Returns the number of items in an array (or object). */
zJSON_PUBLIC(int) zJSON_GetArraySize(const zJSON* array);
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
zJSON_PUBLIC(zJSON) zJSON_GetArrayItem(const zJSON* array, int index);
/* Get item "string" from object. Case insensitive. */
zJSON_PUBLIC(zJSON) zJSON_GetObjectItem(const zJSON* const object, const char* const string);
zJSON_PUBLIC(zJSON) zJSON_GetObjectItemCaseSensitive(const zJSON* const object, const char* const string);
zJSON_PUBLIC(bool) cJSON_HasObjectItem(const zJSON *object, const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when zJSON_Parse() returns 0. 0 when zJSON_Parse() succeeds. */
zJSON_PUBLIC(const char *) zJSON_GetErrorPtr(void);
/* Check item type and return its value */
zJSON_PUBLIC(char*) zJSON_GetStringValue(const zJSON* const json, char* data, size_t size);
zJSON_PUBLIC(size_t) zJSON_GetStringValueSize(const zJSON* const json);
zJSON_PUBLIC(int) zJSON_GetIntValue(const zJSON* const json);
zJSON_PUBLIC(double) zJSON_GetDoubleValue(const zJSON* const json);

/* 变参函数：接收多个字符，以NULL作为结束标志 */
zJSON_PUBLIC(zJSON) zJSON_Get(const zJSON* json, ...);

/* These functions check the type of an item */
zJSON_PUBLIC(bool) zJSON_IsInvalid(const zJSON* const item);
zJSON_PUBLIC(bool) zJSON_IsFalse(zJSON* const item);
zJSON_PUBLIC(bool) zJSON_IsTrue(zJSON* const item);
zJSON_PUBLIC(bool) zJSON_IsBool(const zJSON* const item);
zJSON_PUBLIC(bool) zJSON_IsNull(const zJSON* const item);
zJSON_PUBLIC(bool) zJSON_IsNumber(const zJSON* const item);
zJSON_PUBLIC(bool) zJSON_IsString(const zJSON* const item);
zJSON_PUBLIC(bool) zJSON_IsArray(const zJSON* const item);
zJSON_PUBLIC(bool) zJSON_IsObject(const zJSON* const item);
zJSON_PUBLIC(bool) zJSON_IsRaw(const zJSON* const item);

/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
 * The input pointer json cannot point to a read-only address area, such as a string constant,
 * but should point to a readable and writable address area. */
zJSON_PUBLIC(void) zJSON_Minify(char* json);

#if 0
// 1. 类型判断宏：根据参数类型选择对应的获取函数
#define _ZJSON_CHOOSER(key) _Generic((key), \
    const char*: zJSON_GetObjectItem, \
    char*: zJSON_GetObjectItem, \
    int: zJSON_GetArrayItem, \
    long: zJSON_GetArrayItem \
)
// 2. 递归终止宏：当没有更多参数时，返回当前json对象
#define _ZJSON_GET_END(json) (json)

// 3. 递归处理宏：处理当前参数，然后继续处理剩余参数
#define _ZJSON_GET_NEXT(json, key, ...) \
_ZJSON_GET_CHAIN(_ZJSON_CHOOSER(key)((json), (key)), __VA_ARGS__)

// 4. 核心链式处理宏：根据参数数量选择递归或终止
#define _ZJSON_GET_CHAIN(json, ...) \
    _Generic((int[]){0, ##__VA_ARGS__}, \
    int[1]: _ZJSON_GET_END(json), \
    default: _ZJSON_GET_NEXT(json, __VA_ARGS__) \
)

    // 修正：去掉##，空参数时会生成 {0,}（C99+ 支持）
#define zJSON_Get(json, ...) \
    _Generic((int[]){0, __VA_ARGS__}, \
    int[1]: _ZJSON_GET_END(json),\
    default: _ZJSON_GET_NEXT(json, __VA_ARGS__) \
)

// 5. 对外接口宏
#define zJSON_Get(json, ...) _ZJSON_GET_CHAIN(json, __VA_ARGS__)
#endif

/* The zJSON structure: */
typedef struct
{
    /* The type of the item, as above. */
    int type;
    char* content;
    size_t length;
    size_t offset;
    size_t depth;
    bool fmt;
} zJSONFormatted;
/* Render a zJSON entity to text for transfer/storage. */
zJSON_PUBLIC(zJSONFormatted) zJSON_CreateFormatted(char* content, size_t length);
/* Render a zJSON entity to text for transfer/storage without any formatting. */
zJSON_PUBLIC(zJSONFormatted) zJSON_CreateUnformatted(char* content, size_t length);

zJSON_PUBLIC(bool) zJSONFormatted_IsInvalid(const zJSON* const item);

/* Helper functions for creating and adding items to an object at the same time.
 * They return the added item or NULL on failure. */
zJSON_PUBLIC(bool) zJSON_AddNullToObject(zJSONFormatted* const object, const char* const name);
zJSON_PUBLIC(bool) zJSON_AddTrueToObject(zJSONFormatted* const object, const char* const name);
zJSON_PUBLIC(bool) zJSON_AddFalseToObject(zJSONFormatted* const object, const char* const name);
zJSON_PUBLIC(bool) zJSON_AddBoolToObject(zJSONFormatted* const object, const char* const name, const bool boolean);
zJSON_PUBLIC(bool) zJSON_AddNumberToObject(zJSONFormatted* const object, const char* const name, const double number);
zJSON_PUBLIC(bool) zJSON_AddStringToObject(zJSONFormatted* const object, const char* const name, const char* const string);
zJSON_PUBLIC(bool) zJSON_AddRawToObject(zJSONFormatted* const object, const char* const name, const char* const raw);
zJSON_PUBLIC(bool) zJSON_AddObjectStartToObject(zJSONFormatted* const object, const char* const name);
zJSON_PUBLIC(bool) zJSON_AddObjectEndToObject(zJSONFormatted* const object );
zJSON_PUBLIC(bool) zJSON_AddArrayStartToObject(zJSONFormatted* const object, const char* const name);
zJSON_PUBLIC(bool) zJSON_AddArrayEndToObject(zJSONFormatted* const object );

/* Helper functions for creating and adding items to an object at the same time.
* They return the added item or NULL on failure. */
zJSON_PUBLIC(bool) zJSON_AddTrueToArray(zJSONFormatted* const object );
zJSON_PUBLIC(bool) zJSON_AddFalseToArray(zJSONFormatted* const object );
zJSON_PUBLIC(bool) zJSON_AddBoolToArray(zJSONFormatted* const object, const bool boolean);
zJSON_PUBLIC(bool) zJSON_AddNumberToArray(zJSONFormatted* const object, const double number);
zJSON_PUBLIC(bool) zJSON_AddStringToArray(zJSONFormatted* const object, const char* const string);
zJSON_PUBLIC(bool) zJSON_AddRawToArray(zJSONFormatted* const object, const char* const raw);
zJSON_PUBLIC(bool) zJSON_AddObjectStartToArray(zJSONFormatted* const object);
zJSON_PUBLIC(bool) zJSON_AddObjectEndToArray(zJSONFormatted* const object );
zJSON_PUBLIC(bool) zJSON_AddArrayStartToArray(zJSONFormatted* const object );
zJSON_PUBLIC(bool) zJSON_AddArrayEndToArray(zJSONFormatted* const object );




#ifdef __cplusplus
}
#endif

#endif
